function showError(el, error) {
    const parentElement = el.parentElement
    const errorElement = getErrorElement(el)

    if (error === undefined) {
        errorElement.style.display = 'none'
        //移除父元素上的错误css类样式
        parentElement.classList.remove('has-error')
    } else {
        // Node.textContent 属性表示一个节点及其后代的文本内容
        errorElement.textContent = error
        errorElement.style.display = 'block'
        parentElement.classList.add('has-error')
    }
}

function getErrorElement(el) {
    const parentElement = el.parentElement
    // querySelector方法返回文档中与指定选择器或选择器组匹配的第一个html元素Element，未找到返回null
    let errorElement = parentElement.querySelector('.help-block')

    if (!errorElement) {
        const tpl = `<span class="help-block"></span>`
        // 通过字符串文本的形式创建js片段代码，生成节点插入文档
        const fragment = document.createRange().createContextualFragment(tpl)

        parentElement.appendChild(fragment)
        errorElement = parentElement.querySelector('.help-block')
    }
    return errorElement
}


// 使用元素上绑定的指令修饰符判定要检测的项是否开启
// 指令右侧计算后的值，来做输出显示 
function validate(el, modifers, bindingValue) {
    // 指令绑定值检测，即判断是否为js对象
    bindingValue = bindingValue && typeof bindingValue === 'object' ? bindingValue : {}
    // 若为字符串，获取验证对象的输入内容
    const value = typeof el.value === 'string' ? el.value.trim() : ''
    // 解析指令的js值对象，约定接口{title,error}
    const {
        title = '该项', error
    } = bindingValue

    // 给出错误的默认值
    let defaultError = ''

    // 若存在验证元素必选修饰符，且该元素输入内容为空进入
    if (modifers.required && value === '') {
        // 使用指令值对象
        defaultError = `${title}不能为空`
    } else if (bindingValue.target) {
        // 将选择的目标js元素对象的值与本元素的输入值进行比较（多用于重复密码）
        // 与目标选择器的值比较，target指向一个选择器对象
        const target = document.querySelector(bindingValue.target)
        const targetValue = target ? target.value : null

        if (targetValue !== value) {
            defaultError = `输入的${title}不匹配`
        }
    } else if (bindingValue.regex) {
        // 正则规则验证
        try {
            // 用正则规则去验证被绑定元素输入的内容
            if (!bindingValue.regex.test(value)) {
                defaultError = `${title}格式不正确`
            }
        } catch (e) {
            e instanceof Error
        }
    }

    if (defaultError) {
        if (error === undefined) {
            showError(el, defaultError)
        } else {
            showError(el, error)
        }
    } else {
        showError(el)
    }

}

export default {
    // 钩子函数初始化
    bind(el, binding) {
        // ES6对象解析赋值
        // 指令=的左边 .间隔为包含为修饰符的对象，：间隔为指令参数，
        // 指令=的右边，字符串形式为expression，value参数为右侧js表达式计算后的值
        const {
            value,
            arg,
            modifiers
        } = binding

        // 给定默认事件类型
        const eventType = ['change', 'blur', 'input'].indexOf(arg) !== -1 ? arg : 'change'
        const defaultHandler = () => {
            showError(el)
        }

        // 绑定事件句柄
        const handler = () => {
            validate(el, modifiers, value)
        }

        // 添加多个事件监听器
        el.addEventListener('input', defaultHandler, false)
        el.addEventListener(eventType, handler, false)

        el.destroy = () => {
            // 移除事件监听器
            el.removeEventListener('input', defaultHandler, false)
            el.removeEventListener(eventType, handler, false)
            el.destroy = null
        }
    },

    // 被绑定元素插入父节点时调用
    inserted(el, binding) {
        const {
            value,
            modifiers
        } = binding

        // 查找表单
        // 原生方法 匹配特定选择器且离当前元素最近的祖先元素（也可以是当前元素本身）。如果匹配不到，则返回null
        const form = el.closest(`[data-validator-form]`)
        // 匹配submit表单提交按钮
        const submitBtn = form ? form.querySelector('[type=submit]') : null

        // 按钮上绑定标识属性,及验证调用逻辑
        if (submitBtn) {
            // 绑定验证逻辑
            const submitHandler = () => {
                // value为指令绑定的值，即指令=右边的值
                validate(el, modifiers, value)
                const errors = form.querySelectorAll('.has-error')

                if (!errors.length) {
                    submitBtn.canSubmit = true
                } else {
                    submitBtn.canSubmit = false
                }
            }

            // 提交按钮绑定事件
            submitBtn.addEventListener('click', submitHandler, false)

            // 解除监听器
            el.destroySubmitBtn = () => {
                submitBtn.removeEventListener('click', submitHandler, false)
                el.destroySubmitBtn = null
            }
        }

    },

    // 解绑元素
    unbind(el) {
        el.destroy()
        if (el.destroySubmitBtn) el.destroySubmitBtn()
    }
}